
package com.bluelinelabs.conductor.conductor;


import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.LayoutScatter;
import ohos.app.AbilityContext;
import ohos.app.Context;

import com.bluelinelabs.conductor.conductor.internal.ClassUtils;
import com.bluelinelabs.conductor.conductor.internal.RouterRequiringFunc;
import com.bluelinelabs.conductor.conductor.internal.ViewAttachHandler;
import com.bluelinelabs.conductor.conductor.utile.TextUtils;

import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

/**
 * lipeiquan
 *
 * @since 2021-05-06
 */
public abstract class Controller {

    static final String KEY_VIEW_STATE_BUNDLE = "Controller.viewState.bundle";

    private static final String KEY_CLASS_NAME = "Controller.className";
    private static final String KEY_VIEW_STATE = "Controller.viewState";
    private static final String KEY_CHILD_ROUTERS = "Controller.childRouters";
    private static final String KEY_SAVED_STATE = "Controller.savedState";
    private static final String KEY_INSTANCE_ID = "Controller.instanceId";
    private static final String KEY_TARGET_INSTANCE_ID = "Controller.target.instanceId";
    private static final String KEY_ARGS = "Controller.args";
    private static final String KEY_NEEDS_ATTACH = "Controller.needsAttach";
    private static final String KEY_REQUESTED_PERMISSIONS = "Controller.requestedPermissions";
    private static final String KEY_OVERRIDDEN_PUSH_HANDLER = "Controller.overriddenPushHandler";
    private static final String KEY_OVERRIDDEN_POP_HANDLER = "Controller.overriddenPopHandler";
    private static final String KEY_VIEW_STATE_HIERARCHY = "Controller.viewState.hierarchy";
    private static final String KEY_RETAIN_VIEW_MODE = "Controller.retainViewMode";


    String instanceId;
    private final IntentParams args;
    private final List<LifecycleListener> lifecycleListeners = new ArrayList<>();
    private Router router;
    private IntentParams savedInstanceState;
    private final ArrayList<RouterRequiringFunc> onRouterSetListeners = new ArrayList<>();
    private boolean needsAttach;
    private boolean attached;
    private ControllerChangeHandler overriddenPushHandler;
    private Component component;
    private final List<ControllerHostedRouter> childRouters = new ArrayList<>();
    boolean isBeingDestroyed;
    boolean isDetachFrozen;
    boolean viewWasDetached;
    private boolean destroyed;
    private boolean attachedToUnownedParent;
    private boolean isPerformingExitTransition;
    IntentParams viewState;
    private ViewAttachHandler viewAttachHandler;
    boolean viewIsAttached;
    private Controller parentController;
    private boolean awaitingParentAttach;
    private boolean hasSavedViewState;
    private boolean hasOptionsMenu;
    private boolean optionsMenuHidden;
    private ControllerChangeHandler overriddenPopHandler;
    private WeakReference<Component> destroyedView;
    private boolean isContextAvailable;
    private String targetInstanceId;
    private final ArrayList<String> requestedPermissions = new ArrayList<>();
    private RetainViewMode retainViewMode = RetainViewMode.RELEASE_DETACH;


    static Controller newInstance(IntentParams bundle) {
        final String className = (String) bundle.getParam(KEY_CLASS_NAME);
        //noinspection ConstantConditions
        Class cls = ClassUtils.classForName(className, false);
        Constructor[] constructors = cls.getConstructors();
        Constructor bundleConstructor = getBundleConstructor(constructors);

        IntentParams args = (IntentParams) bundle.getParam(KEY_ARGS);
        if (args != null) {
            args.setClassLoader(cls.getClassLoader());
        }
        Controller controller;
        try {
            if (bundleConstructor != null) {
                controller = (Controller) bundleConstructor.newInstance(args);
            } else {
                //noinspection ConstantConditions
                controller = (Controller) getDefaultConstructor(constructors).newInstance();

                // Restore the args that existed before the last process death
                if (args != null) {
                    Iterator<String> iterator = args.keySet().iterator();
                    while (iterator.hasNext()) {
                        String key = iterator.next();
                        controller.args.setParam(key, args.getParam(key));
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("An exception occurred while creating a new instance of " + className + ". " + e.getMessage(), e);
        }

        controller.restoreInstanceState(bundle);
        return controller;
    }


    protected Controller() {
        this(null);
    }


    protected Controller(IntentParams args) {
        this.args = args != null ? args : new IntentParams();
        if (this.args.getClassLoader() != null) {
            this.args.setClassLoader(getClass().getClassLoader());
        }
        instanceId = UUID.randomUUID().toString();
        ensureRequiredConstructor();
    }

    public void setTargetController(Controller target) {
        if (targetInstanceId != null) {
            throw new RuntimeException("Target controller already set. A controller's target may only be set once.");
        }

        targetInstanceId = target != null ? target.getInstanceId() : null;
    }


    public final Controller getTargetController() {
        if (targetInstanceId != null) {
            return router.getRootRouter().getControllerWithInstanceId(targetInstanceId);
        }
        return null;
    }


    final void activityPaused(AbilityContext activity) {
        onActivityPaused(activity);
    }

    final Controller findController(String instanceId) {
        if (this.instanceId.equals(instanceId)) {
            return this;
        }

        for (Router router : childRouters) {
            Controller matchingChild = router.getControllerWithInstanceId(instanceId);
            if (matchingChild != null) {
                return matchingChild;
            }
        }
        return null;
    }


    final void activityResumed(AbilityContext activity) {
        if (!attached && component != null && viewIsAttached) {
            attach(component);
        } else if (attached) {
            needsAttach = false;
            hasSavedViewState = false;
        }

        onActivityResumed(activity);
    }


    public final boolean isAttached() {
        System.out.println("wtf Controller isAttached attached = " + attached);
        return attached;
    }

    private void ensureRequiredConstructor() {
        Constructor[] constructors = getClass().getConstructors();
        System.out.println("wtf " + getClass() + " ensureRequiredConstructor constructors = " + constructors.length + " getBundleConstructor(constructors)  = " + getBundleConstructor(constructors) + " getDefaultConstructor(constructors) = " + getDefaultConstructor(constructors));
        if (getBundleConstructor(constructors) == null && getDefaultConstructor(constructors) == null) {
            throw new RuntimeException(getClass() + " does not have a constructor that takes a Bundle argument or a default constructor. Controllers must have one of these in order to restore their states.");
        }
    }

    private void restoreInstanceState(IntentParams savedInstanceState) {
        viewState = (IntentParams) savedInstanceState.getParam(KEY_VIEW_STATE);
        if (viewState != null) {
            viewState.setClassLoader(getClass().getClassLoader());
        }

        instanceId = (String) savedInstanceState.getParam(KEY_INSTANCE_ID);
        targetInstanceId = (String) savedInstanceState.getParam(KEY_TARGET_INSTANCE_ID);
        requestedPermissions.addAll((List) savedInstanceState.getParam(KEY_REQUESTED_PERMISSIONS));
        overriddenPushHandler = ControllerChangeHandler.fromBundle((IntentParams) savedInstanceState.getParam(KEY_OVERRIDDEN_PUSH_HANDLER));
        overriddenPopHandler = ControllerChangeHandler.fromBundle((IntentParams) savedInstanceState.getParam(KEY_OVERRIDDEN_POP_HANDLER));
        needsAttach = (boolean) savedInstanceState.getParam(KEY_NEEDS_ATTACH);
        retainViewMode = RetainViewMode.values()[savedInstanceState.getParam(KEY_RETAIN_VIEW_MODE) != null ? (int) savedInstanceState.getParam(KEY_RETAIN_VIEW_MODE) : 0];

        List<IntentParams> childBundles = (ArrayList) savedInstanceState.getParam(KEY_CHILD_ROUTERS);
        for (IntentParams childBundle : childBundles) {
            ControllerHostedRouter childRouter = new ControllerHostedRouter();
            childRouter.setHostController(this);
            childRouter.restoreInstanceState(childBundle);
            childRouters.add(childRouter);
        }

        this.savedInstanceState = (IntentParams) savedInstanceState.getParam(KEY_SAVED_STATE);
        if (this.savedInstanceState != null) {
            this.savedInstanceState.setClassLoader(getClass().getClassLoader());
        }
        performOnRestoreInstanceState();
    }


    final void changeStarted(ControllerChangeHandler changeHandler, ControllerChangeType changeType) {
        if (!changeType.isEnter) {
            isPerformingExitTransition = true;
            for (ControllerHostedRouter router : childRouters) {
                router.setDetachFrozen(true);
            }
        }

        onChangeStarted(changeHandler, changeType);

        List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
        for (LifecycleListener lifecycleListener : listeners) {
            lifecycleListener.onChangeStart(this, changeHandler, changeType);
        }
    }

    final void changeEnded(ControllerChangeHandler changeHandler, ControllerChangeType changeType) {
        if (!changeType.isEnter) {
            isPerformingExitTransition = false;
            for (ControllerHostedRouter router : childRouters) {
                router.setDetachFrozen(false);
            }
        }

        onChangeEnded(changeHandler, changeType);

        List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
        for (LifecycleListener lifecycleListener : listeners) {
            lifecycleListener.onChangeEnd(this, changeHandler, changeType);
        }

        if (isBeingDestroyed && !viewIsAttached && !attached && destroyedView != null) {
            Component view = destroyedView.get();
            if (router.container != null && view != null && view.getComponentParent() == router.container) {
                router.container.removeComponent(view);
            }
            destroyedView = null;
        }

        changeHandler.onEnd();
    }

    private static Constructor getBundleConstructor(Constructor[] constructors) {
        for (Constructor constructor : constructors) {
            if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0] == IntentParams.class) {
                return constructor;
            }
        }
        return null;
    }

    final void setParentController(Controller controller) {
        parentController = controller;
    }

    private static Constructor getDefaultConstructor(Constructor[] constructors) {
        System.out.println("wtf getDefaultConstructor constructors siez = " + constructors.length);
        for (Constructor constructor : constructors) {
            if (constructor.getParameterTypes().length == 0) {
                return constructor;
            }
        }
        return null;
    }

    final void setRouter(Router router) {
        if (this.router != router) {
            this.router = router;

            performOnRestoreInstanceState();

            for (RouterRequiringFunc listener : onRouterSetListeners) {
                listener.execute();
            }
            onRouterSetListeners.clear();
        } else {
            performOnRestoreInstanceState();
        }
    }


    public final void setOptionsMenuHidden(boolean optionsMenuHidden) {
        boolean invalidate = attached && hasOptionsMenu && this.optionsMenuHidden != optionsMenuHidden;

        this.optionsMenuHidden = optionsMenuHidden;

        if (invalidate) {
            router.invalidateOptionsMenu();
        }
    }


    public boolean handleBack() {
        List<RouterTransaction> childTransactions = new ArrayList<>();

        for (ControllerHostedRouter childRouter : childRouters) {
            childTransactions.addAll(childRouter.getBackstack());
        }

        Collections.sort(childTransactions, new Comparator<RouterTransaction>() {
            @Override
            public int compare(RouterTransaction o1, RouterTransaction o2) {
                return o2.getTransactionIndex() - o1.getTransactionIndex();
            }
        });

        for (RouterTransaction transaction : childTransactions) {
            Controller childController = transaction.controller;

            if (childController.isAttached() && childController.getRouter().handleBack()) {
                return true;
            }
        }

        return false;
    }


    public final Controller getParentController() {
        return parentController;
    }


    public final List<Router> getChildRouters() {
        List<Router> routers = new ArrayList<>(childRouters.size());
        routers.addAll(childRouters);
        return routers;
    }

    final void destroy() {
        destroy(false);
    }

    private void destroy(boolean removeViews) {
        isBeingDestroyed = true;

        if (router != null) {
            router.unregisterForActivityResults(instanceId);
        }

        for (ControllerHostedRouter childRouter : childRouters) {
            childRouter.destroy(false);
        }

        if (!attached) {
            removeViewReference();
        } else if (removeViews) {
            detach(component, true, false);
        }
    }

    public final void registerForActivityResult(final int requestCode) {
        executeWithRouter(new RouterRequiringFunc() {
            @Override
            public void execute() {
                router.registerForActivityResult(instanceId, requestCode);
            }
        });
    }

    final void executeWithRouter(RouterRequiringFunc listener) {
        if (router != null) {
            listener.execute();
        } else {
            onRouterSetListeners.add(listener);
        }
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    }


    private void removeViewReference() {
        if (component != null) {
            if (!isBeingDestroyed && !hasSavedViewState) {
                saveViewState(component);
            }

            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.preDestroyView(this, component);
            }

            onDestroyView(component);

            // viewAttachHandler may be null iff the controller was popped before we got here
            if (viewAttachHandler != null) {
                viewAttachHandler.unregisterAttachListener(component);
            }

            viewAttachHandler = null;
            viewIsAttached = false;

            if (isBeingDestroyed) {
                destroyedView = new WeakReference<>(component);
            }
            component = null;

            listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.postDestroyView(this);
            }

            for (ControllerHostedRouter childRouter : childRouters) {
                childRouter.removeHost();
            }
        }

        if (isBeingDestroyed) {
            performDestroy();
        }
    }

    private void performDestroy() {
        if (isContextAvailable) {
            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.preContextUnavailable(this, getActivity());
            }

            isContextAvailable = false;
            onContextUnavailable();

            listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.postContextUnavailable(this);
            }
        }

        if (!destroyed) {
            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.preDestroy(this);
            }

            destroyed = true;

            onDestroy();

            parentController = null;

            listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.postDestroy(this);
            }
        }
    }

    public final void removeChildRouter(Router childRouter) {
        if ((childRouter instanceof ControllerHostedRouter) && childRouters.remove(childRouter)) {
            childRouter.destroy(true);
        }
    }


    private void saveViewState(Component view) {
        hasSavedViewState = true;

        viewState = new IntentParams();
        viewState.setClassLoader(getClass().getClassLoader());

        HashMap<Integer, String> hierarchyState = new HashMap<>();
//        view.saveHierarchyState(hierarchyState);
        viewState.setParam(KEY_VIEW_STATE_HIERARCHY, hierarchyState);

        IntentParams stateBundle = new IntentParams();
        stateBundle.setClassLoader(getClass().getClassLoader());
//        onSaveViewState(view, stateBundle);
        viewState.setParam(KEY_VIEW_STATE_BUNDLE, stateBundle);

        List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
        for (LifecycleListener lifecycleListener : listeners) {
            lifecycleListener.onSaveViewState(this, viewState);
        }
    }

    public final Router getRouter() {
        return router;
    }

    protected void onDetach(Component view) {
    }


    protected void onContextUnavailable() {
    }


    public final AbilityContext getActivity() {
        return router != null ? router.getActivity() : null;
    }

    public ControllerChangeHandler getOverriddenPopHandler() {
        return overriddenPopHandler;
    }


    protected void onChangeEnded(ControllerChangeHandler changeHandler, ControllerChangeType changeType) {
    }


    public final Router getChildRouter(ComponentContainer container) {
        return getChildRouter(container, null);
    }

    public final Router getChildRouter(ComponentContainer container, String tag) {
        //noinspection ConstantConditions
        return getChildRouter(container, tag, true);
    }

    public final Router getChildRouter(ComponentContainer container, String tag, boolean createIfNeeded) {
        final int containerId = container.getId();
        if (containerId == ohos.agp.components.Component.ID_DEFAULT) {
            throw new IllegalStateException("You must set an id on your container.");
        }

        ControllerHostedRouter childRouter = null;
        for (ControllerHostedRouter router : childRouters) {
            if (router.getHostId() == containerId && TextUtils.equals(tag, router.getTag())) {
                childRouter = router;
                break;
            }
        }

        if (childRouter == null) {
            if (createIfNeeded) {
                childRouter = new ControllerHostedRouter(container.getId(), tag);
                childRouter.setHostContainer(this, container);
                childRouters.add(childRouter);

                if (isPerformingExitTransition) {
                    childRouter.setDetachFrozen(true);
                }
            }
        } else if (!childRouter.hasHost()) {
            childRouter.setHostContainer(this, container);
            childRouter.rebindIfNeeded();
        }

        return childRouter;
    }

    final void activityDestroyed(AbilityContext activity) {
        if (activity.isUpdatingConfigurations()) {
            detach(component, true, false);
        } else {
            destroy(true);
        }

        if (isContextAvailable) {
            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.preContextUnavailable(this, activity);
            }

            isContextAvailable = false;
            onContextUnavailable();

            listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.postContextUnavailable(this);
            }
        }
    }


    protected void onActivityResumed(AbilityContext activity) {
    }

    public final boolean isDestroyed() {
        System.out.println("wtf isDestroyed  destroyed = " + destroyed);
        return destroyed;
    }

    final void setNeedsAttach(boolean needsAttach) {
        this.needsAttach = needsAttach;
    }

    void detach(Component view, boolean forceViewRefRemoval, boolean blockViewRefRemoval) {
        if (!attachedToUnownedParent) {
            for (ControllerHostedRouter router : childRouters) {
                router.prepareForHostDetach();
            }
        }

        final boolean removeViewRef = !blockViewRefRemoval && (forceViewRefRemoval || retainViewMode == RetainViewMode.RELEASE_DETACH || isBeingDestroyed);

        if (attached) {
            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.preDetach(this, view);
            }

            attached = false;

            if (!awaitingParentAttach) {
                onDetach(view);
            }

            if (hasOptionsMenu && !optionsMenuHidden) {
                router.invalidateOptionsMenu();
            }

            listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.postDetach(this, view);
            }
        }

        if (removeViewRef) {
            removeViewReference();
        }
    }

    public final boolean isBeingDestroyed() {
        return isBeingDestroyed;
    }

    final Component inflate(ComponentContainer parent) {
        if (component != null && component.getComponentParent() != null && component.getComponentParent() != parent) {
            detach(component, true, false);
            removeViewReference();
        }
        if (component == null) {
            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.preCreateView(this);
            }

            IntentParams savedViewState = viewState == null ? null : (IntentParams) viewState.getParam(KEY_VIEW_STATE_BUNDLE);
            component = onCreateView(LayoutScatter.getInstance(parent.getContext()), parent, savedViewState);
            if (component == parent) {
                throw new IllegalStateException("Controller's onCreateView method returned the parent ViewGroup. Perhaps you forgot to pass false for LayoutInflater.inflate's attachToRoot parameter?");
            }
            listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.postCreateView(this, component);
            }

            restoreViewState(component);

            if (!isBeingDestroyed) {
                viewAttachHandler = new ViewAttachHandler(new ViewAttachHandler.ViewAttachListener() {
                    @Override
                    public void onAttached() {
                        viewIsAttached = true;
                        viewWasDetached = false;
                        attach(component);
                    }

                    @Override
                    public void onDetached(boolean fromActivityStop) {
                        viewIsAttached = false;
                        viewWasDetached = true;

                        if (!isDetachFrozen) {
                            detach(component, false, fromActivityStop);
                        }
                    }

                    @Override
                    public void onViewDetachAfterStop() {
                        if (!isDetachFrozen) {
                            detach(component, false, false);
                        }
                    }
                });
                viewAttachHandler.listenForAttach(component);
            }
        } else {
            restoreChildControllerHosts();
        }

        return component;
    }

    private void restoreChildControllerHosts() {
        for (ControllerHostedRouter childRouter : childRouters) {
            if (!childRouter.hasHost()) {
                Component containerView = component.findComponentById(childRouter.getHostId());

                if (containerView instanceof ComponentContainer) {
                    childRouter.setHostContainer(this, (ComponentContainer) containerView);
                    childRouter.rebindIfNeeded();
                }
            }
        }
    }

    final void activityStarted(AbilityContext activity) {
        if (viewAttachHandler != null) {
            viewAttachHandler.onActivityStarted();
        }

        onActivityStarted(activity);
    }

    final void setDetachFrozen(boolean frozen) {
        if (isDetachFrozen != frozen) {
            isDetachFrozen = frozen;

            for (ControllerHostedRouter router : childRouters) {
                router.setDetachFrozen(frozen);
            }

            if (!frozen && component != null && viewWasDetached) {
                Component aView = component;
                detach(component, false, false);
                if (component == null && aView.getComponentParent() == router.container) {
                    router.container.removeComponent(aView); // need to remove the view when this controller is a child controller
                }
            }
        }
    }

    private void restoreViewState(Component view) {
        if (viewState != null) {
            ;
//            view.restoreHierarchyState(viewState.getSparseParcelableArray(KEY_VIEW_STATE_HIERARCHY));
//            Bundle savedViewState = viewState.getBundle(KEY_VIEW_STATE_BUNDLE);
//            savedViewState.setClassLoader(getClass().getClassLoader());
//            onRestoreViewState(view, savedViewState);
//
//            restoreChildControllerHosts();

            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.onRestoreViewState(this, viewState);
            }
        }
    }

    void attach(Component view) {
        attachedToUnownedParent = router == null || view.getComponentParent() != router.container;
        if (attachedToUnownedParent || isBeingDestroyed) {
            return;
        }

        if (parentController != null && !parentController.attached) {
            awaitingParentAttach = true;
            return;
        } else {
            awaitingParentAttach = false;
        }

        hasSavedViewState = false;

        List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
        for (LifecycleListener lifecycleListener : listeners) {
            lifecycleListener.preAttach(this, view);
        }

        attached = true;
        needsAttach = router.isActivityStopped;

        onAttach(view);

        if (hasOptionsMenu && !optionsMenuHidden) {
            router.invalidateOptionsMenu();
        }

        listeners = new ArrayList<>(lifecycleListeners);
        for (LifecycleListener lifecycleListener : listeners) {
            lifecycleListener.postAttach(Controller.this, view);
        }

        for (ControllerHostedRouter childRouter : childRouters) {
            for (RouterTransaction childTransaction : childRouter.backstack) {
                if (childTransaction.controller.awaitingParentAttach) {
                    childTransaction.controller.attach(childTransaction.controller.component);
                }
            }

            if (childRouter.hasHost()) {
                childRouter.rebindIfNeeded();
            }
        }
    }

    protected void onActivityStarted(AbilityContext activity) {
    }

    final boolean getNeedsAttach() {
        return needsAttach;
    }

    final void onContextAvailable() {
        System.out.println("wtf onContextAvailable ");
//        final Context context = router.getActivity();
//
//        if (context != null && !isContextAvailable) {
//            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
//            for (LifecycleListener lifecycleListener : listeners) {
//                lifecycleListener.preContextAvailable(this);
//            }
//
//            isContextAvailable = true;
//            onContextAvailable(context);
//
//            listeners = new ArrayList<>(lifecycleListeners);
//            for (LifecycleListener lifecycleListener : listeners) {
//                lifecycleListener.postContextAvailable(this, context);
//            }
//        }
//
//        for (Router childRouter : childRouters) {
//            childRouter.onContextAvailable();
//        }
    }

    public IntentParams getArgs() {
        return args;
    }

    private void performOnRestoreInstanceState() {
        if (savedInstanceState != null && router != null) {
            onRestoreInstanceState(savedInstanceState);

            List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
            for (LifecycleListener lifecycleListener : listeners) {
                lifecycleListener.onRestoreInstanceState(this, savedInstanceState);
            }

            savedInstanceState = null;
        }
    }

    final void prepareForHostDetach() {
        needsAttach = needsAttach || attached;

        for (ControllerHostedRouter router : childRouters) {
            router.prepareForHostDetach();
        }
    }

    final void activityStopped(AbilityContext activity) {
        final boolean attached = this.attached;

        if (viewAttachHandler != null) {
            viewAttachHandler.onActivityStopped();
        }

        if (attached && activity.isUpdatingConfigurations()) {
            needsAttach = true;
        }

        onActivityStopped(activity);
    }

    final IntentParams saveInstanceState() {
        if (!hasSavedViewState && component != null) {
            saveViewState(component);
        }

        IntentParams outState = new IntentParams();
        outState.setParam(KEY_CLASS_NAME, getClass().getName());
        outState.setParam(KEY_VIEW_STATE, viewState);
        outState.setParam(KEY_ARGS, args);
        outState.setParam(KEY_INSTANCE_ID, instanceId);
        outState.setParam(KEY_TARGET_INSTANCE_ID, targetInstanceId);
        outState.setParam(KEY_REQUESTED_PERMISSIONS, requestedPermissions);
        outState.setParam(KEY_NEEDS_ATTACH, needsAttach || attached);
        outState.setParam(KEY_RETAIN_VIEW_MODE, retainViewMode.ordinal());

        if (overriddenPushHandler != null) {
            outState.setParam(KEY_OVERRIDDEN_PUSH_HANDLER, overriddenPushHandler.toBundle());
        }
        if (overriddenPopHandler != null) {
            outState.setParam(KEY_OVERRIDDEN_POP_HANDLER, overriddenPopHandler.toBundle());
        }

        ArrayList<IntentParams> childBundles = new ArrayList<>(childRouters.size());
        for (ControllerHostedRouter childRouter : childRouters) {
            IntentParams routerBundle = new IntentParams();
            childRouter.saveInstanceState(routerBundle);
            childBundles.add(routerBundle);
        }
        outState.setParam(KEY_CHILD_ROUTERS, childBundles);

        IntentParams savedState = new IntentParams();
        savedState.setClassLoader(getClass().getClassLoader());
        onSaveInstanceState(savedState);

        List<LifecycleListener> listeners = new ArrayList<>(lifecycleListeners);
        for (LifecycleListener lifecycleListener : listeners) {
            lifecycleListener.onSaveInstanceState(this, savedState);
        }

        outState.setParam(KEY_SAVED_STATE, savedState);

        return outState;
    }


    protected void onRestoreInstanceState(IntentParams savedInstanceState) {
    }

    protected void onDestroyView(Component component) {
    }

    protected void onDestroy() {
    }


    protected void onActivityStopped(AbilityContext activity) {
    }


    protected void onAttach(Component view) {
    }

    protected void onChangeStarted(ControllerChangeHandler changeHandler, ControllerChangeType changeType) {
    }

    protected void onSaveInstanceState(IntentParams outState) {
    }


    public final Component getView() {
        return component;
    }


    public final void addLifecycleListener(LifecycleListener lifecycleListener) {
        if (!lifecycleListeners.contains(lifecycleListener)) {
            lifecycleListeners.add(lifecycleListener);
        }
    }

    public final String getInstanceId() {
        return instanceId;
    }

    protected void onActivityPaused(AbilityContext activity) {
    }


    public final ControllerChangeHandler getOverriddenPushHandler() {
        return overriddenPushHandler;
    }

    protected abstract Component onCreateView(LayoutScatter inflater, ComponentContainer container, IntentParams savedViewState);


    protected void onSaveViewState(Component view, IntentParams outState) {
    }

    /**
     * Modes that will influence when the Controller will allow its view to be destroyed
     */
    public enum RetainViewMode {
        /**
         * The Controller will release its reference to its view as soon as it is detached.
         */
        RELEASE_DETACH,
        /**
         * The Controller will retain its reference to its view when detached, but will still release the reference when a config change occurs.
         */
        RETAIN_DETACH
    }


    /**
     * Allows external classes to listen for lifecycle events in a Controller
     */
    public static abstract class LifecycleListener {

        public void onChangeStart(Controller controller, ControllerChangeHandler changeHandler, ControllerChangeType changeType) {
        }

        public void onChangeEnd(Controller controller, ControllerChangeHandler changeHandler, ControllerChangeType changeType) {
        }

        public void preCreateView(Controller controller) {
        }

        public void postCreateView(Controller controller, Component component) {

        }

        public void preAttach(Controller controller, Component component) {
        }

        public void postAttach(Controller controller, Component component) {
        }

        public void preDetach(Controller controller, Component component) {
        }

        public void postDetach(Controller controller, Component component) {
        }

        public void preDestroyView(Controller controller, Component component) {
        }

        public void postDestroyView(Controller controller) {
        }

        public void preDestroy(Controller controller) {
        }

        public void postDestroy(Controller controller) {
        }

        public void preContextAvailable(Controller controller) {
        }

        public void postContextAvailable(Controller controller, Context context) {
        }

        public void preContextUnavailable(Controller controller, Context context) {
        }

        public void postContextUnavailable(Controller controller) {
        }

        public void onSaveInstanceState(Controller controller, IntentParams outState) {
        }

        public void onRestoreInstanceState(Controller controller, IntentParams savedInstanceState) {
        }

        public void onSaveViewState(Controller controller, IntentParams outState) {
        }

        public void onRestoreViewState(Controller controller, IntentParams savedViewState) {
        }

    }

}
